import type {
  AxiosInstance,
  AxiosRequestConfig,
  CustomParamsSerializer,
  AxiosResponse,
  AxiosError,
  Method
} from 'axios'
import Axios from 'axios'
import { stringify } from 'qs'

import NProgress from '../progress'
import { useUserStore } from '@/stores/user'

const defaultConfig: AxiosRequestConfig = {
  // 请求超时时间
  timeout: 10000,
  headers: {
    Accept: 'application/json, text/plain, */*',
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest'
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: {
    serialize: stringify as unknown as CustomParamsSerializer
  }
}
const formatToken = (token: string): string => {
  return 'Bearer ' + token
}
class MHttp {
  constructor() {
    this.httpInterceptorsRequest()
    this.httpInterceptorsResponse()
  }
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig)

  private httpInterceptorsRequest(): void {
    MHttp.axiosInstance.interceptors.request.use(
      async (config: AxiosRequestConfig): Promise<any> => {
        // 开启进度条动画
        NProgress.start()
        /** 请求白名单，放置一些不需要token的接口（通过设置请求白名单，防止token过期后再请求造成的死循环问题） */
        const whiteList = ['/bind', '/sms', '/captcha', '/code']
        return whiteList.some((v) => config.url!.indexOf(v) > -1)
          ? config
          : new Promise((resolve, reject) => {
              const token = useUserStore().getToken()
              if (token && token !== undefined && token !== '') {
                config.headers!['Authorization'] = formatToken(token)
                resolve(config)
              } else {
                reject({ message: '登录信息已过期' })
              }
            })
      },
      (error) => {
        return Promise.reject(error)
      }
    )
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    const instance = MHttp.axiosInstance
    instance.interceptors.response.use(
      (response: AxiosResponse) => {
        // 关闭进度条动画
        NProgress.done()
        return response
      },
      (error: AxiosError) => {
        const $error = error
        // 关闭进度条动画
        NProgress.done()
        // 所有的响应异常 区分来源为取消请求/非取消请求
        return Promise.reject($error)
      }
    )
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: AxiosRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as AxiosRequestConfig

    // 单独处理自定义请求/响应回调
    return new Promise((resolve, reject) => {
      MHttp.axiosInstance
        .request(config)
        .then((response: AxiosResponse) => {
          if (typeof response.data == 'string') {
            resolve(JSON.parse(response.data))
          } else {
            resolve(response.data)
          }
        })
        .catch((error) => {
          reject(error)
        })
    })
  }

  /** 单独抽离的post工具函数 */
  public post<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>('post', url, params, config)
  }

  /** 单独抽离的get工具函数 */
  public get<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: AxiosRequestConfig
  ): Promise<P> {
    return this.request<P>('get', url, params, config)
  }
}
export type RequestMethods = Extract<
  Method,
  'get' | 'post' | 'put' | 'delete' | 'patch' | 'option' | 'head'
>
export const http = new MHttp()
